/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "discover.h"

#include "common.h"
#include "discovery_service.h"
#include "ohos_bt_gap.h"

#define MAX_FOUND_DEVICE_NUM 32

typedef struct {
    uint32_t num;
    DeviceInfo dev[MAX_FOUND_DEVICE_NUM];
} DeviceList;

static sem_t g_publishSem;
static sem_t g_subscribeSem;
static bool g_needShowFoundDevice = false;
static bool g_isStartDiscoverySucc = false;
static DeviceList g_deviceList = { 0, {0} };

bool __attribute__ ((weak)) GetLocalAddr(unsigned char *mac, unsigned int len)
{
    (void)mac;
    (void)len;
    printf("\n<< ATTENTION !!! >> GetLocalAddr WEAK FUNC!!!\n");
    return true;
}

static void OnPublishSuccess(int publishId)
{
    Logf(INFO, ">>>OnPublishSuccess publishId = %d.\n", publishId);
    sem_post(&g_publishSem);
}

static void OnPublishFail(int publishId, PublishFailReason reason)
{
    Logf(ERROR, ">>>OnPublishFail publishId = %d, reason = %d.\n", publishId, reason);
    sem_post(&g_publishSem);
}

void D_PublishService(void)
{
    sem_init(&g_publishSem, 0, 0);
    PublishInfo info = {
        .publishId = 1,
        .medium = BLE,
        .mode = DISCOVER_MODE_ACTIVE,
        .freq = MID,
        .capability = "dvKit",
        .capabilityData = (unsigned char *)"capdata1",
        .dataLen = 9,
    };
    info.publishId = GetInputNumber("Please input publish id:");
    info.medium = GetInputNumber("Please input publish medium(0 - AUTO, 1 - BLE, 2 - COAP):");
    int choice = GetInputNumber("Please input publish mode(0 - DISCOVER_MODE_PASSIVE, 1 - DISCOVER_MODE_ACTIVE):");
    if (choice == 0) {
        info.mode = DISCOVER_MODE_PASSIVE;
    } else if (choice == 1) {
        info.mode = DISCOVER_MODE_ACTIVE;
    } else {
        Logf(WARN, "unknown mode choice %d, use default DISCOVER_MODE_ACTIVE", choice);
    }
    choice = GetInputNumber("Please input publish capability(0 - hicall \n"
                                                            "1 - profile \n"
                                                            "2 - homevisionPic \n"
                                                            "3 - castPlus \n"
                                                            "4 - aaCapability \n"
                                                            "5 - dvKit (default) \n"
                                                            "6 - ddmpCapability \n"
                                                            "7 - osdCapability \n"
                                                            "):");
    info.capability = g_capabilityMap[choice].capability;

    IPublishCallback cb = {
        .OnPublishSuccess = OnPublishSuccess,
        .OnPublishFail = OnPublishFail,
    };
    int ret = PublishService(PKG_NAME, &info, &cb);
    if (ret != 0) {
        Logf(ERROR, "PublishService fail, ret = %d.", ret);
        sem_destroy(&g_publishSem);
        return;
    }
    sem_wait(&g_publishSem);
    sem_destroy(&g_publishSem);
}

void D_UnPublishService(void)
{
    int publishId = GetInputNumber("Please input publish id:");
    int ret = UnPublishService(PKG_NAME, publishId);
    if (ret != 0) {
        Logf(ERROR, "UnPublishService fail, ret = %d.", ret);
    }
}

static void ClearFoundDevices(void)
{
    (void)memset_s(&g_deviceList, sizeof(DeviceList), 0, sizeof(DeviceList));
}

static bool RecordFoundDevice(const DeviceInfo *device)
{
    for (int i = 0; i < MAX_FOUND_DEVICE_NUM; i++) {
        if (memcmp(&device->addr[0], &g_deviceList.dev[i].addr[0], sizeof(ConnectionAddr)) == 0) {
            return false;
        }
    }
    (void)memcpy_s(&g_deviceList.dev[g_deviceList.num], sizeof(DeviceInfo), device, sizeof(DeviceInfo));
    g_deviceList.num++;
    if (g_deviceList.num >= MAX_FOUND_DEVICE_NUM) {
        g_deviceList.num = 0;
    }
    return true;
}

static void OnDeviceFound(const DeviceInfo *device)
{
    if (device == NULL || !g_needShowFoundDevice || !RecordFoundDevice(device)) {
        return;
    }

    for (unsigned int i = 0; i < device->addrNum; i++) {
        switch (device->addr[i].type) {
            case CONNECTION_ADDR_BLE:
                Logf(INFO, ">>>OnDeviceFound: BLE MAC - %s  |  DevId - %s", device->addr[i].info.ble.bleMac, device->devId);
                break;
            case CONNECTION_ADDR_BR:
                Logf(INFO, ">>>OnDeviceFound: BR MAC - %s  |  DevId - %s", device->addr[i].info.br.brMac, device->devId);
                break;
            case CONNECTION_ADDR_ETH:
            case CONNECTION_ADDR_WLAN:
                Logf(INFO, ">>>OnDeviceFound: IP ADDR:Port - %s:%d  |  DevId - %s", device->addr[i].info.ip.ip,
                    device->addr[i].info.ip.port, device->devId);
                break;
            default:
                Logf(ERROR, ">>>OnDeviceFound: unknown device type, %d", device->addr[i].type);
                break;
        }
    }
    for(int i = 0; i < device->capabilityBitmapNum; i++) {
        Logf(INFO, ">>>OnDeviceFound: capability-%d: %d", i, device->capabilityBitmap[i]);
    }
}

static void OnDiscoverFailed(int subscribeId, DiscoveryFailReason reason)
{
    Logf(INFO, ">>>OnDiscoverFailed subscribeId = %d, reason = %d.", subscribeId, reason);
    sem_post(&g_subscribeSem);
}

static void OnDiscoverySuccess(int subscribeId)
{
    printf(">>>OnDiscoverySuccess subscribeId = %d.\n", subscribeId);
    g_isStartDiscoverySucc = true;
    sem_post(&g_subscribeSem);
}

void D_StartDiscovery(void)
{
    sem_init(&g_subscribeSem, 0, 0);
    SubscribeInfo info = {
        .subscribeId = 1,
        .medium = BLE,
        .mode = DISCOVER_MODE_ACTIVE,
        .freq = MID,
        .capability = "dvKit",
        .capabilityData = (unsigned char *)"capdata1",
        .dataLen = 9,
        .isSameAccount = false,
        .isWakeRemote = false,
    };
    info.subscribeId = GetInputNumber("Please input subscribe id:");
    info.medium = GetInputNumber("Please input subscribe medium(0 - AUTO, 1 - BLE, 2 - COAP):");
    int choice = GetInputNumber("Please input publish mode(0 - DISCOVER_MODE_PASSIVE, 1 - DISCOVER_MODE_ACTIVE):");
    if (choice == 0) {
        info.mode = DISCOVER_MODE_PASSIVE;
    } else if (choice == 1) {
        info.mode = DISCOVER_MODE_ACTIVE;
    } else {
        Logf(WARN, "unknown mode choice %d, use default DISCOVER_MODE_ACTIVE", choice);
    }
    choice = GetInputNumber("Please input publish capability(0 - hicall \n"
                                                            "1 - profile \n"
                                                            "2 - homevisionPic \n"
                                                            "3 - castPlus \n"
                                                            "4 - aaCapability \n"
                                                            "5 - dvKit (default) \n"
                                                            "6 - ddmpCapability \n"
                                                            "7 - osdCapability \n"
                                                            "):");
    info.capability = g_capabilityMap[choice].capability;

    IDiscoveryCallback cb = {
        .OnDeviceFound = OnDeviceFound,
        .OnDiscoverFailed = OnDiscoverFailed,
        .OnDiscoverySuccess = OnDiscoverySuccess,
    };
    ClearFoundDevices();
    g_needShowFoundDevice = true;
    g_isStartDiscoverySucc = false;
    int ret = StartDiscovery(PKG_NAME, &info, &cb);
    if (ret != 0) {
        Logf(ERROR, "StartDiscovery fail, ret = %d.", ret);
        sem_destroy(&g_subscribeSem);
        return;
    }
    sem_wait(&g_subscribeSem);
    if (g_isStartDiscoverySucc) {
        WaitInputEnter("Found Devices shown below, please input ENTER key to break.\n");
        g_needShowFoundDevice = false;
    }
    sem_destroy(&g_subscribeSem);
}

void D_StopDiscovery(void)
{
    int subscribeId = GetInputNumber("Please input subscribe id:");
    int ret = StopDiscovery(PKG_NAME, subscribeId);
    if (ret != 0) {
        Logf(ERROR, "StopDiscovery fail, ret = %d.", ret);
    }
}

void D_GetBtMac(void)
{
    unsigned char addr[MAC_SIZE] = {0};
    if (!GetLocalAddr(addr, MAC_SIZE)) {
        Logf(ERROR, "GetLocalAddr fail");
        return;
    }
    Logf(INFO, "BT MAC - ["MAC_STR"]", MAC_ADDR(addr));
}
